home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / HPACK78S.ZIP / arcdir.c < prev    next >
C/C++ Source or Header  |  1992-12-03  |  44KB  |  1,485 lines

  1. /****************************************************************************
  2. *                                                                            *
  3. *                          HPACK Multi-System Archiver                        *
  4. *                          ===========================                        *
  5. *                                                                            *
  6. *                       Archive Directory Handling Routines                    *
  7. *                          ARCDIR.C  Updated 10/06/92                        *
  8. *                                                                            *
  9. * This program is protected by copyright and as such any use or copying of    *
  10. *  this code for your own purposes directly or indirectly is highly uncool    *
  11. *                    and if you do so there will be....trubble.                *
  12. *               And remember: We know where your kids go to school.            *
  13. *                                                                            *
  14. *        Copyright 1989 - 1992  Peter C.Gutmann.  All rights reserved        *
  15. *                                                                            *
  16. ****************************************************************************/
  17.  
  18. #include <ctype.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #ifdef __MAC__
  22.   #include "defs.h"
  23.   #include "arcdir.h"
  24.   #include "error.h"
  25.   #include "flags.h"
  26.   #include "hpacklib.h"
  27.   #include "system.h"
  28.   #include "tags.h"
  29.   #include "fastio.h"
  30. #else
  31.   #include "defs.h"
  32.   #include "arcdir.h"
  33.   #include "error.h"
  34.   #include "flags.h"
  35.   #include "hpacklib.h"
  36.   #include "system.h"
  37.   #include "tags.h"
  38.   #include "io/fastio.h"
  39. #endif /* __MAC__ */
  40.  
  41. #if defined( __MSDOS__ ) && !defined( GUI )
  42.  
  43. /* Prototypes for memory management functions */
  44.  
  45. void markMem( void );
  46. void releaseMem( void );
  47. void ungetMem( void );
  48.  
  49. #endif /* __MSDOS__ && !GUI */
  50.  
  51. /* The following are declared in ARCDIRIO.C */
  52.  
  53. extern PARTSIZELIST *partSizeStartPtr, *partSizeCurrPtr;
  54. extern BOOLEAN arcdirCorrupted;
  55.  
  56. /* The following are declared in FRONTEND.C */
  57.  
  58. extern char archiveFileName[];
  59.  
  60. /* The data structure for the name table for fileNames */
  61.  
  62. typedef struct NT {
  63.                   char *namePtr;        /* Pointer to the name */
  64.                   WORD dirIndex;        /* Directory index of this filename */
  65.                   WORD hType;            /* Type of this file */
  66.                   struct NT *next;        /* The next entry in this list */
  67.                   } NT_ENTRY;
  68.  
  69. /* The size of the memory buffer for archive information */
  70.  
  71. #ifdef ARCHIVE_TYPE
  72.   #define MEM_BUFSIZE    1000    /* More modest memory usage in test vers. */
  73. #else
  74.   #define MEM_BUFSIZE    8000
  75. #endif /* ARCHIVE_TYPE */
  76.  
  77. /* The size of the hash table.  Must be a power of 2 */
  78.  
  79. #define HASHTABLE_SIZE    4096
  80.  
  81. /****************************************************************************
  82. *                                                                            *
  83. *                                Global Variables                            *
  84. *                                                                            *
  85. ****************************************************************************/
  86.  
  87. FILEHDRLIST *fileHdrStartPtr, *fileHdrCurrPtr;
  88.         /* Start and current position in the list of file headers */
  89.  
  90. /* vvv This'll be the first thing up against the wall when the rewrite comes */
  91. DIRHDRLIST **dirHdrList;        /* Table to hold directory structure */
  92. int currDirHdrListIndex;        /* Current pos.in list of dir.headers */
  93. int currEntry;                    /* Current index into directory array */
  94. int lastEntry;                    /* Last used directory in dir.array */
  95.  
  96. static NT_ENTRY **hashTable;    /* The table for hashing into nameTable */
  97.  
  98. long fileDataOffset;            /* Offset of current file data from start of
  99.                                    archive */
  100. long dirDataOffset;                /* Offset of current directory data from start
  101.                                    of directory data area */
  102.  
  103. WORD fileLinkNo, dirLinkNo;        /* Current maximum dir and file link ID */
  104.  
  105. /****************************************************************************
  106. *                                                                            *
  107. *                        Archive Directory Memory Usage                        *
  108. *                                                                            *
  109. ****************************************************************************/
  110.  
  111. /* Struct sizes are as follows:
  112.  
  113.     FILEHDR            19 bytes        DIRHDR             11 bytes
  114.     FILEHDRLIST        43 bytes        DIRHDRLIST        43 bytes
  115.     NT_ENTRY        12 bytes                      ----------
  116.                   ----------                        54 bytes + dirName
  117.                     65 bytes + fileName
  118.  
  119.    Data structure sizes are as follows:
  120.  
  121.     fileHdrList        entries limited by memory
  122.     dirHdrList        8000 bytes, 2000 entries
  123.     hashTable        16K bytes, 4096 entries (fixed size)
  124.     nameTable        entries limited by memory
  125.     --------------------------------------------------------
  126.     Total :          24,384 bytes */
  127.  
  128. /****************************************************************************
  129. *                                                                            *
  130. *                            General Work Functions                            *
  131. *                                                                            *
  132. ****************************************************************************/
  133.  
  134. /* Add a file header to the lists of file headers */
  135.  
  136. static char *fileNamePtr;
  137.  
  138. void addFileHeader( FILEHDR *theHeader, WORD hType, BYTE extraInfo, WORD linkID )
  139.     {
  140.     FILEHDRLIST *fileHdrPrevPtr;
  141.     WORD dirNo;
  142.  
  143.     /* Link the header into the list */
  144.     if( fileHdrStartPtr == NULL )
  145.         fileHdrCurrPtr = fileHdrStartPtr = ( FILEHDRLIST * ) hmalloc( sizeof( FILEHDRLIST ) );
  146.     else
  147.         {
  148.         fileHdrCurrPtr->next = ( FILEHDRLIST * ) hmalloc( sizeof( FILEHDRLIST ) );
  149.         fileHdrCurrPtr = fileHdrCurrPtr->next;
  150.         }
  151.     if( fileHdrCurrPtr == NULL )
  152.         error( OUT_OF_MEMORY );
  153.     fileHdrCurrPtr->next = NULL;
  154.     fileHdrCurrPtr->data = *theHeader;
  155.     fileHdrCurrPtr->offset = fileDataOffset;
  156.     fileHdrCurrPtr->hType = hType;
  157.     fileHdrCurrPtr->fileName = fileNamePtr;
  158.     fileDataOffset += theHeader->dataLen + theHeader->auxDataLen;
  159.     fileHdrCurrPtr->tagged = FALSE;
  160.  
  161.     /* Allocate room for extraInfo if necessary */
  162.     if( extraInfo )
  163.         {
  164.         if( ( fileHdrCurrPtr->extraInfo = \
  165.                 ( BYTE * ) hmalloc( getExtraInfoLen( extraInfo ) + 1 ) ) == NULL )
  166.             error( OUT_OF_MEMORY );
  167.         *fileHdrCurrPtr->extraInfo = extraInfo;
  168.         }
  169.     else
  170.         fileHdrCurrPtr->extraInfo = NULL;
  171.  
  172.     /* Update the head and tail pointers and link in the new header to the
  173.        list of files in this directory */
  174.     if( ( dirNo = theHeader->dirIndex ) > lastEntry )
  175.         {
  176.         /* Bad entry, move to ROOT_DIR */
  177.         dirNo = theHeader->dirIndex = ROOT_DIR;
  178.         arcdirCorrupted = TRUE;
  179.         }
  180.     if( ( fileHdrPrevPtr = dirHdrList[ dirNo ]->fileInDirListTail ) == NULL )
  181.         /* Set up new list if necessary */
  182.         dirHdrList[ dirNo ]->fileInDirListHead = fileHdrCurrPtr;
  183.     else
  184.         fileHdrPrevPtr->nextFile = fileHdrCurrPtr;
  185.     dirHdrList[ dirNo ]->fileInDirListTail = fileHdrCurrPtr;
  186.     fileHdrCurrPtr->prevFile = fileHdrPrevPtr;
  187.     fileHdrCurrPtr->nextFile = NULL;
  188.  
  189.     /* Link the new file to the list of linked files if necessary */
  190.     if( extraInfo & EXTRA_LINKED )
  191.         {
  192.         /* Find new link and add it */
  193. /*        addLinkInfo( NO_LINK ); */
  194. /*        Don't process links yet */
  195.         fileHdrCurrPtr->linkID = linkID;
  196.         fileHdrCurrPtr->nextLink = fileHdrCurrPtr->prevLink = NULL;
  197.         }
  198.     else
  199.         {
  200.         fileHdrCurrPtr->linkID = NO_LINK;
  201.         fileHdrCurrPtr->nextLink = fileHdrCurrPtr->prevLink = NULL;
  202.         }
  203.     }
  204.  
  205. /* Remove a file header from the lists of file headers */
  206.  
  207. void deleteFileHeader( FILEHDRLIST *theHeader )
  208.     {
  209.     FILEHDRLIST *nextPtr = theHeader->nextFile, *prevPtr = theHeader->prevFile;
  210.     WORD dirNo = theHeader->data.dirIndex;
  211.  
  212.     /* Unlink this header from the list of files in this directory */
  213.     if( prevPtr == NULL )
  214.         {
  215.         /* Delete from start of list */
  216.         if( dirHdrList[ dirNo ]->fileInDirListTail == theHeader )
  217.             /* Only header in list, set list to empty */
  218.             dirHdrList[ dirNo ]->fileInDirListTail = NULL;
  219.         dirHdrList[ dirNo ]->fileInDirListHead = nextPtr;
  220.         }
  221.     else
  222.         /* Delete from middle/end of list */
  223.         prevPtr->nextFile = nextPtr;
  224.     if( nextPtr != NULL )
  225.         nextPtr->prevFile = prevPtr;
  226.  
  227.     /* Free the header and associated extraInfo and fileName */
  228.     hfree( theHeader->fileName );
  229.     if( theHeader->data.archiveInfo & ARCH_EXTRAINFO )
  230.         hfree( theHeader->extraInfo );
  231.     hfree( theHeader );
  232.     }
  233.  
  234. /* Add a fileName to the fileNameList, checking for duplicate names */
  235.  
  236. static WORD oldHashValue;
  237. static NT_ENTRY *nameTablePtr;
  238.  
  239. BOOLEAN addFileName( const WORD dirIndex, const WORD hType, const char *fileName )
  240.     {
  241.     int ch = TRUE;
  242.     WORD hashValue = hType;
  243.     NT_ENTRY *tblEntry;
  244.     int fileNameLen = 0;
  245.     BOOLEAN isUnicode = FALSE;
  246.     WORD *wordPtr = ( WORD * ) mrglBuffer;
  247.  
  248.     /* Check whether we're reading a Unicode string */
  249.     if( ( fileName == NULL ) && ( fileHdrCurrPtr->extraInfo != NULL ) && \
  250.         ( *fileHdrCurrPtr->extraInfo & EXTRA_UNICODE ) )
  251.         isUnicode = TRUE;
  252.  
  253.     /* Insert the string in the name table.  A possible optimization at this
  254.        point is that if the filename is already in the filename list to not
  255.        insert it a second time but merely point to the first filename.  The
  256.        problem with this is that if the first filename is hfree()'d at a later
  257.        point the second reference will point to hyperspace */
  258.     while( ch )
  259.         {
  260.         /* Get the next char in the name from the appropriate location.
  261.            Hashing the full name is probably a waste for > 5-odd chars,
  262.            but we still need to grovel through them all to get them into the
  263.            fileName list */
  264.         if( fileName == NULL )
  265.             {
  266.             /* Read in char with sanity check:  With an archive which has been
  267.                corrupted just right we may go into an infinite loop of reading
  268.                EOF's or garbage here */
  269.             if( isUnicode )
  270.                 {
  271.                 if( ( ch = fgetWord() ) == FEOF || fileNameLen >= DIRBUFSIZE / sizeof( WORD ) )
  272.                     {
  273.                     arcdirCorrupted = TRUE;
  274.                     return( FALSE );
  275.                     }
  276.                 }
  277.             else
  278.                 {
  279.                 if( ( ch = fgetByte() ) == FEOF || fileNameLen >= DIRBUFSIZE )
  280.                     {
  281.                     arcdirCorrupted = TRUE;
  282.                     return( FALSE );
  283.                     }
  284.                 }
  285.             }
  286.         else
  287.             ch = *fileName++;
  288.  
  289. #if defined( __AMIGA__ ) || defined( __ARC__ ) || defined( __MAC__ ) || \
  290.     defined( __OS2__ ) || defined( __UNIX__ )
  291.         /* Check if we want to smash case */
  292.         if( sysSpecFlags & SYSPEC_FORCELOWER )
  293.             ch = tolower( ch );
  294. #endif /* __AMIGA__ || __ARC__ || __MAC__ || __OS2__ || __UNIX__ */
  295.  
  296.         if( ch )    /* Don't hash the final '\0' */
  297.             hashValue = ( hashValue << 1 ) ^ ch;
  298.         if( isUnicode )
  299.             wordPtr[ fileNameLen++ ] = ch;
  300.         else
  301.             mrglBuffer[ fileNameLen++ ] = ch;
  302.         }
  303.     hashValue &= HASHTABLE_SIZE - 1;                /* A faster MOD */
  304.  
  305.     /* Search the name table, with chaining in case of a collision */
  306.     tblEntry = hashTable[ hashValue ];
  307.     while( tblEntry != NULL )
  308.         if( !strcmp( tblEntry->namePtr, ( char * ) mrglBuffer ) && \
  309.                      tblEntry->dirIndex == dirIndex && tblEntry->hType == hType )
  310.             return( /* tblEntry */ TRUE );    /* Already exists, exit */
  311.         else
  312.             tblEntry = tblEntry->next;
  313.  
  314.     /* Now add the new entry to the start of the list pointed to by hashtable
  315.        after remembering the current state for possible future use.  The chains
  316.        are organised in LRU order for no obvious reason */
  317.     oldHashValue = hashValue;
  318.     if( ( fileNamePtr = ( char * ) hmalloc( fileNameLen ) ) == NULL )
  319.         error( OUT_OF_MEMORY );
  320.     strcpy( fileNamePtr, ( char * ) mrglBuffer );
  321.     if( ( nameTablePtr = ( NT_ENTRY * ) hmalloc( sizeof( NT_ENTRY ) ) ) == NULL )
  322.         error( OUT_OF_MEMORY );
  323.     nameTablePtr->namePtr = fileNamePtr;
  324.     nameTablePtr->dirIndex = dirIndex;
  325.     nameTablePtr->hType = hType;
  326.     nameTablePtr->next = tblEntry;
  327.     hashTable[ hashValue ] = nameTablePtr;
  328.  
  329.     /* Link the fileName to the header now if we can */
  330.     if( fileName == NULL )
  331.         fileHdrCurrPtr->fileName = fileNamePtr;
  332.  
  333.     return( FALSE );
  334.     }
  335.  
  336. /* Delete the last filename entered */
  337.  
  338. void deleteLastFileName( void )
  339.     {
  340.     hashTable[ oldHashValue ] = nameTablePtr->next;
  341. #if defined( __MSDOS__ ) && !defined( GUI )
  342.     /* Undo the last hmalloc.  This works because there are no hmalloc()'s
  343.        between the addFileName() and deleteFileName() calls in FRONTEND.C */
  344.     ungetMem();        /* Undo fileName malloc */
  345.     ungetMem();        /* Undo nameTable malloc */
  346. #else
  347.     hfree( fileNamePtr );
  348. #endif /* __MSDOS__ && !GUI */
  349.     }
  350.  
  351. /* Add a section size for a multipart archive */
  352.  
  353. void addPartSize( const long segmentEnd )
  354.     {
  355.     /* Link the new part size into the list */
  356.     if( partSizeStartPtr == NULL )
  357.         partSizeCurrPtr = partSizeStartPtr = ( PARTSIZELIST * ) hmalloc( sizeof( PARTSIZELIST ) );
  358.     else
  359.         {
  360.         partSizeCurrPtr->next = ( PARTSIZELIST * ) hmalloc( sizeof( PARTSIZELIST ) );
  361.         partSizeCurrPtr = partSizeCurrPtr->next;
  362.         }
  363.     if( partSizeCurrPtr == NULL )
  364.         error( OUT_OF_MEMORY );
  365.     partSizeCurrPtr->next = NULL;
  366.     partSizeCurrPtr->segEnd = segmentEnd;
  367.     }
  368.  
  369. /* Determine which archive part a given data offset is in */
  370.  
  371. int getPartNumber( const long offset )
  372.     {
  373.     PARTSIZELIST *partSizePtr;
  374.     int index;
  375.  
  376.     /* Find the archive segment in which the offset value falls */
  377.     for( partSizePtr = partSizeStartPtr, index = 0; \
  378.          partSizePtr != NULL && offset >= partSizePtr->segEnd; \
  379.          partSizePtr = partSizePtr->next, index++ );
  380.  
  381.     return( index );
  382.     }
  383.  
  384. /* Determine the end of a given segment */
  385.  
  386. long getPartSize( int partNumber )
  387.     {
  388.     PARTSIZELIST *partSizePtr;
  389.  
  390.     for( partSizePtr = partSizeStartPtr; partNumber; partNumber-- )
  391.         partSizePtr = partSizePtr->next;
  392.     return( ( partSizePtr == NULL ) ? endPosition : partSizePtr->segEnd );
  393.     }
  394.  
  395. /* Get a given part of an archive */
  396.  
  397. void readArcTrailer( void );
  398.  
  399. void getPart( const int thePart )
  400.     {
  401.     /* Ask for the wanted archive part */
  402.     hclose( archiveFD );
  403.     archiveFD = IO_ERROR;        /* Mark it as invalid */
  404.     multipartWait( 0, thePart );
  405.     while( ( archiveFD = hopen( archiveFileName, O_RDONLY | S_DENYWR | A_RANDSEQ ) ) == ERROR )
  406.         multipartWait( 0, thePart );
  407.     setInputFD( archiveFD );
  408.     readArcTrailer();
  409.  
  410.     /* Make sure we've got the right part */
  411.     while( thePart != currPart )
  412.         {
  413.         hclose( archiveFD );
  414.         archiveFD = IO_ERROR;    /* Mark it as invalid */
  415.         multipartWait( 0, thePart );
  416.         while( ( archiveFD = hopen( archiveFileName, O_RDONLY | S_DENYWR | A_RANDSEQ ) ) == ERROR )
  417.             multipartWait( 0, thePart );
  418.         setInputFD( archiveFD );
  419.         readArcTrailer();
  420.         }
  421.     }
  422.  
  423. /* Set up the vars used */
  424.  
  425. void initArcDir( void )
  426.     {
  427.     if( ( hashTable = ( NT_ENTRY ** ) hmalloc( HASHTABLE_SIZE * sizeof( NT_ENTRY * ) ) ) == NULL || \
  428.         ( dirHdrList = ( DIRHDRLIST ** ) hmalloc( MEM_BUFSIZE ) ) == NULL )
  429.         error( OUT_OF_MEMORY );
  430.  
  431.     /* Initially there is no list of headers - this initial reset is necessary
  432.        to stop resetArcDir() trying to free nonexistant header lists */
  433.     fileHdrStartPtr = NULL;
  434.     partSizeStartPtr = NULL;
  435.  
  436. #if !defined( __MSDOS__ ) || defined( GUI )
  437.     /* Set dirHdrList to empty so we don't try and free it in freeHdrLists() */
  438.     dirHdrList[ ROOT_DIR ] = NULL;
  439. #endif /* !__MSDOS__ || GUI */
  440.     }
  441.  
  442. #if !defined( __MSDOS__ ) || defined( GUI )
  443.  
  444. /* Free the mem used in the list of headers + strings */
  445.  
  446. static void freeHdrLists( void )
  447.     {
  448.     FILEHDRLIST *fileHdrCursor = fileHdrStartPtr, *fileHdrHeaderPtr;
  449.     PARTSIZELIST *partSizeCursor = partSizeStartPtr, *partSizeHeaderPtr;
  450.     NT_ENTRY *nameTblCursor, *nameTblPtr;
  451.     WORD theEntry, nextEntry;
  452.  
  453.     /* Free the fileHdr list */
  454.     while( fileHdrCursor != NULL )
  455.         {
  456.         fileHdrHeaderPtr = fileHdrCursor;
  457.         fileHdrCursor = fileHdrCursor->next;
  458.         hfree( fileHdrHeaderPtr->fileName );
  459.         if( fileHdrHeaderPtr->data.archiveInfo & ARCH_EXTRAINFO )
  460.             hfree( fileHdrHeaderPtr->extraInfo );
  461.         hfree( fileHdrHeaderPtr );
  462.         }
  463.  
  464.     /* Free the dirHdr list */
  465.     if( dirHdrList[ ROOT_DIR ] != NULL )
  466.         for( theEntry = dirHdrList[ ROOT_DIR ]->next; theEntry != END_MARKER; \
  467.              theEntry = nextEntry )
  468.             {
  469.             nextEntry = dirHdrList[ theEntry ]->next;
  470.             hfree( dirHdrList[ theEntry ]->dirName );
  471.             hfree( dirHdrList[ theEntry ] );
  472.             }
  473.  
  474.     /* Free the name table lists.  We must be careful not to free the
  475.        associated fileNames since they have already been freed as part of
  476.        the fileHdr freeing process */
  477.     for( theEntry = 0; theEntry < HASHTABLE_SIZE; theEntry++ )
  478.         {
  479.         nameTblCursor = hashTable[ theEntry ];
  480.         while( nameTblCursor != NULL )
  481.             {
  482.             nameTblPtr = nameTblCursor;
  483.             nameTblCursor = nameTblCursor->next;
  484.             hfree( nameTblPtr );
  485.             }
  486.         }
  487.  
  488.     /* Free the archive part size list */
  489.     while( partSizeCursor != NULL )
  490.         {
  491.         partSizeHeaderPtr = partSizeCursor;
  492.         partSizeCursor = partSizeCursor->next;
  493.         hfree( partSizeHeaderPtr );
  494.         }
  495.  
  496.     /* Reset header list pointer */
  497.     fileHdrStartPtr = NULL;
  498.     partSizeStartPtr = NULL;
  499.     }
  500.  
  501. void endArcDir( void )
  502.     {
  503.     freeHdrLists();
  504.     hfree( dirHdrList );
  505.     hfree( hashTable );
  506.     }
  507. #else
  508.  
  509. void resetArcDirMem( void )
  510.     {
  511.     releaseMem();    /* Ah, the joys of a single-user OS */
  512.     }
  513. #endif /* !__MSDOS__ || GUI */
  514.  
  515. /* Reset various pointers and arrays for each new archive handled */
  516.  
  517. static void resetLinkCache( void );
  518.  
  519. void resetArcDir( void )
  520.     {
  521.     DIRHDRLIST *rootDirPtr;
  522.     int theEntry;
  523.  
  524.     /* Clear out the hash table */
  525.     for( theEntry = 0; theEntry < HASHTABLE_SIZE; theEntry++ )
  526.         hashTable[ theEntry ] = NULL;
  527.  
  528.     /* Set up initial table indices */
  529.     currDirHdrListIndex = 1;            /* Room for ROOT_DIR header */
  530.     currEntry = lastEntry = ROOT_DIR;   /* Entries added off root dir */
  531.     currPart = lastPart = 0;            /* Only one part to archive */
  532.  
  533.     /* Free the various lists of headers */
  534. #if !defined( __MSDOS__ ) || defined( GUI )
  535.     freeHdrLists();
  536. #else
  537.     fileHdrStartPtr = NULL;
  538.     partSizeStartPtr = NULL;    /* Reset header list pointers */
  539.     markMem();    /* Cut back the heap to this point later on */
  540. #endif /* !__MSDOS__ || GUI */
  541.     fileDataOffset = dirDataOffset = 0L;    /* Begin at start of archive */
  542.     fileLinkNo = dirLinkNo = NO_LINK + 1;    /* Set up initial magic link no. */
  543.     resetLinkCache();            /* Clear link cache */
  544.  
  545.     /* Create a dummy header for the root directory */
  546.     if( ( rootDirPtr = dirHdrList[ ROOT_DIR ] = \
  547.         ( DIRHDRLIST * ) hmalloc( sizeof( DIRHDRLIST ) ) ) == NULL )
  548.         error( OUT_OF_MEMORY );
  549.     rootDirPtr->dirIndex = ROOT_DIR;
  550.     rootDirPtr->next = END_MARKER;
  551.     rootDirPtr->data.dirInfo = 0;
  552.     rootDirPtr->data.dirTime = 0L;
  553.     rootDirPtr->data.dataLen = 0L;
  554.     rootDirPtr->data.parentIndex = ROOT_DIR;
  555.     rootDirPtr->fileInDirListHead = rootDirPtr->fileInDirListTail = NULL;
  556.     rootDirPtr->dirInDirListHead = rootDirPtr->dirInDirListTail = NULL;
  557.     rootDirPtr->nextDir = rootDirPtr->prevDir = NULL;
  558.     rootDirPtr->nextLink = rootDirPtr->prevLink = NULL;
  559.     rootDirPtr->dirName = ( char * ) hmalloc( 1 );    /* No need for NULL chk */
  560.     *rootDirPtr->dirName = '\0';    /* The dir with no name */
  561.     rootDirPtr->offset = 0L;
  562.     rootDirPtr->hType = DIRTYPE_NORMAL;
  563.     rootDirPtr->linkID = NO_LINK;
  564.     rootDirPtr->tagged = FALSE;
  565.  
  566.     /* Reset the directory data buffer */
  567.     dirBufCount = 0;
  568.     }
  569.  
  570. /* Return the current state of the archive directory data structures.
  571.    doFixEverything can safely be initialized statically since as soon
  572.    as it is set to FALSE we exit due to the error processing */
  573.  
  574. BOOLEAN doFixEverything = TRUE;
  575.  
  576. void getArcdirState( FILEHDRLIST **hdrListEnd, int *dirEnd )
  577.     {
  578.     *hdrListEnd = fileHdrCurrPtr;
  579.     *dirEnd = currEntry;
  580.     }
  581.  
  582. /* Set the current archive directory structures.  The doFixEverything flag
  583.    must be set to false at this point to make sure we don't call
  584.    fixEverything() when performing error recovery, since calling
  585.    fixEverything() will rebuild the directory tree into an invalid (read:
  586.    stuffed) state */
  587.  
  588. void setArcdirState( FILEHDRLIST *hdrListEnd, const int dirEnd )
  589.     {
  590. #if !defined( __MSDOS__ ) || defined( GUI )
  591.     FILEHDRLIST *fileHdrCursor = hdrListEnd->next, *fileHdrPtr;
  592. #endif /* !__MSDOS__ || GUI */
  593.  
  594.     if( hdrListEnd != NULL )    /* This may be NULL if no files */
  595.         {
  596. #if !defined( __MSDOS__ ) || defined( GUI )
  597.         /* Remove all file headers after the cutoff point */
  598.         while( fileHdrCursor != NULL )
  599.             {
  600.             fileHdrPtr = fileHdrCursor;
  601.             fileHdrCursor = fileHdrCursor->next;
  602.             deleteFileHeader( fileHdrPtr );
  603.             }
  604. #endif /* !__MSDOS__ || GUI */
  605.         hdrListEnd->next = NULL;
  606.         }
  607.     dirHdrList[ dirEnd ]->next = END_MARKER;
  608.     doFixEverything = FALSE;
  609.     }
  610.  
  611. /* Get the length of the compressed data in the archive */
  612.  
  613. long getCoprDataLength( void )
  614.     {
  615.     return( fileDataOffset );
  616.     }
  617.  
  618. /****************************************************************************
  619. *                                                                            *
  620. *                    File/Directory Link Handling Functions                    *
  621. *                                                                            *
  622. ****************************************************************************/
  623.  
  624. /* The link cache management code.  This is used to speed lookups for
  625.    link ID's.  If we have a cache miss (only under exceptional circumstances),
  626.    a linear search of the file list is done */
  627.  
  628. #ifdef __MSDOS__
  629.   #define CACHE_SIZE        16
  630. #else
  631.   #define CACHE_SIZE        64
  632. #endif /* __MSDOS__ */
  633.  
  634. typedef struct {
  635.                WORD linkID;            /* The link ID */
  636.                void *linkChain;        /* The link chain it points to */
  637.                } CACHE_ENTRY;
  638.  
  639. CACHE_ENTRY linkCache[ CACHE_SIZE ];
  640. int cacheIndex;
  641. BOOLEAN cacheFull;
  642.  
  643. /* Clear the link cache */
  644.  
  645. static void resetLinkCache( void )
  646.     {
  647.     int i;
  648.  
  649.     for( i = 0; i < CACHE_SIZE; i++ )
  650.         linkCache[ i ].linkID = NO_LINK;
  651.     cacheIndex = 0;
  652.     cacheFull = FALSE;
  653.     }
  654.  
  655. /* Look up a link ID in the link cache */
  656.  
  657. static int linkCacheLookup( const WORD linkID )
  658.     {
  659.     int i;
  660.  
  661.     /* Search cache for matching link ID */
  662.     for( i = 0; i < cacheIndex; i++ )
  663.         if( linkCache[ i ].linkID == linkID )
  664.             return( i );
  665.  
  666.     /* No match found in cache */
  667.     return( ERROR );
  668.     }
  669.  
  670. /* Update the cache entries */
  671.  
  672. static void updateLinkCache( int cachePos )
  673.     {
  674.     int newPos = cachePos >> 1, i;
  675.     CACHE_ENTRY theEntry = linkCache[ cachePos ];
  676.  
  677.     /* Move all the cache entries back one and move the entry to update
  678.        to the newly vacated position */
  679.     for( i = cachePos; i > newPos; i-- )
  680.         linkCache[ i ] = linkCache[ i - 1 ];
  681.     linkCache[ newPos ] = theEntry;
  682.     }
  683.  
  684. /* Enter a link ID in the cache */
  685.  
  686. static int addLinkToCache( WORD linkID, void *linkChain )
  687.     {
  688.     if( cacheFull )
  689.         {
  690.         /* Overwrite the LRU entry */
  691.         linkCache[ CACHE_SIZE - 1 ].linkID = linkID;
  692.         linkCache[ CACHE_SIZE - 1 ].linkChain = linkChain;
  693.         return( CACHE_SIZE - 1 );
  694.         }
  695.     else
  696.         {
  697.         /* Just add the entry in the next available position */
  698.         linkCache[ cacheIndex ].linkID = linkID;
  699.         linkCache[ cacheIndex++ ].linkChain = linkChain;
  700.         if( cacheIndex == CACHE_SIZE )
  701.             cacheFull = TRUE;
  702.         return( cacheIndex - 1 );
  703.         }
  704.     }
  705.  
  706. /* Find the link chain corresponding to a given file/directory link ID */
  707.  
  708. FILEHDRLIST *findFileLinkChain( WORD linkID )
  709.     {
  710.     FILEHDRLIST *fileHdrCursor;
  711.     int index;
  712.  
  713.     linkID &= ~LINK_ANCHOR;        /* Mask out anchor bit */
  714.  
  715.     /* Try and find the linkID in the cache */
  716.     if( ( index = linkCacheLookup( linkID ) ) == ERROR )
  717.         {
  718.         /* Not in cache, need to do linear search of file header list.
  719.            Note that we always find a match since we've just added the
  720.            header with this linkID to the chain */
  721.         for( fileHdrCursor = fileHdrStartPtr; fileHdrCursor != NULL; \
  722.              fileHdrCursor = fileHdrCursor->next )
  723.             if( fileHdrCursor->linkID == linkID )
  724.                 break;
  725.  
  726.         index = addLinkToCache( linkID, fileHdrCursor );
  727.         }
  728.     else
  729.         fileHdrCursor = ( FILEHDRLIST * ) linkCache[ index ].linkChain;
  730.  
  731.     /* Update the position of the cache entry */
  732.     updateLinkCache( index );
  733.     return( fileHdrCursor );
  734.     }
  735.  
  736. DIRHDRLIST *findDirLinkChain( WORD linkID )
  737.     {
  738.     DIRHDRLIST *dirHdrCursor;
  739.     WORD theEntry;
  740.     int index;
  741.  
  742.     linkID &= ~LINK_ANCHOR;        /* Mask out anchor bit */
  743.  
  744.     /* Try and find the linkID in the cache */
  745.     if( ( index = linkCacheLookup( linkID ) ) == ERROR )
  746.         {
  747.         /* Not in cache, need to do linear search of dir header list.
  748.            Note that we always find a match since we've just added the
  749.            header with this linkID to the chain */
  750.         for( theEntry = dirHdrList[ ROOT_DIR ]->next; theEntry != END_MARKER; \
  751.              theEntry = dirHdrList[ theEntry ]->next )
  752.             if( dirHdrList[ theEntry ]->linkID == linkID )
  753.                 break;
  754.         dirHdrCursor = dirHdrList[ theEntry ];
  755.  
  756.         index = addLinkToCache( linkID, dirHdrCursor );
  757.         }
  758.     else
  759.         dirHdrCursor = ( DIRHDRLIST * ) linkCache[ index ].linkChain;
  760.  
  761.     /* Update the position of the cache entry */
  762.     updateLinkCache( index );
  763.     return( dirHdrCursor );
  764.     }
  765.  
  766. #ifdef __UNIX__
  767.  
  768. /* Unix-specific link-management code.  It would be nice to be able to
  769.    cache link lookups here too but the inodes are usually unique so
  770.    the cache won't work - we need to use a slow linear search.
  771.  
  772.    All links found in this manner are hard links, and will have the anchor-
  773.    node bit set.  Soft links are dependant links, and are handled
  774.    seperately */
  775.  
  776. /* Check if the file link ID's are identical.  The file link ID's are the
  777.    device number and inode */
  778.  
  779. FILEHDRLIST *linkFileHdr;
  780.  
  781. BOOLEAN checkForLink( const LONG fileLinkID )
  782.     {
  783.     /* Step along the chain of headers checking if the linkID's are identical */
  784.     for( linkFileHdr = fileHdrStartPtr; linkFileHdr != NULL; \
  785.          linkFileHdr = linkFileHdr->next )
  786.         if( linkFileHdr->fileLinkID == fileLinkID )
  787.             return( TRUE );
  788.  
  789.     return( FALSE );
  790.     }
  791.  
  792. /* Add the link ID to a file header */
  793.  
  794. void addLinkInfo( const LONG linkID )
  795.     {
  796.     /* Step along the chain of headers checking if the linkID's are identical */
  797.     for( linkFileHdr = fileHdrStartPtr; linkFileHdr != NULL; \
  798.          linkFileHdr = linkFileHdr->next )
  799.         if( linkFileHdr->linkID == linkID )
  800.             {
  801.             /* Check if there is already a link chain set up for this link ID */
  802.             if( linkFileHdr->fileLinkID == NO_LINK )
  803.                 {
  804.                 /* No, set the link number for the two linked files */
  805.                 linkFileHdr->fileLinkID = fileHdrCurrPtr->fileLinkID = \
  806.                                           fileLinkNo | LINK_ANCHOR;
  807.                 fileLinkNo++;
  808.                 }
  809.             else
  810.                 {
  811.                 /* Go to the end of the link chain and add the link number */
  812.                 while( linkFileHdr->nextLink != NULL )
  813.                     linkFileHdr = linkFileHdr->nextLink;
  814.                 fileHdrCurrPtr->fileLinkID = linkFileHdr->fileLinkID;
  815.                 }
  816.  
  817.             /* Add the header to the link chain */
  818.             linkFileHdr->nextLink = fileHdrCurrPtr;
  819.             fileHdrCurrPtr->prevLink = linkFileHdr;
  820.             fileHdrCurrPtr->nextLink = NULL;
  821.  
  822.             return;
  823.             }
  824.  
  825.     /* No link found, set up header in isolation */
  826.     fileHdrCurrPtr->nextLink = NULL;
  827.     fileHdrCurrPtr->prevLink = NULL;
  828.     }
  829. #endif /* __UNIX__ */
  830.  
  831. /****************************************************************************
  832. *                                                                            *
  833. *                        Archive Directory Handling Functions                *
  834. *                                                                            *
  835. ****************************************************************************/
  836.  
  837. /* Pointers into the the fileHdrList and dirHdrList, used by the
  838.    getFirst/NextFileEntry() and getFirst/NextDirEntry() routines */
  839.  
  840. static FILEHDRLIST *filePtr;
  841. static DIRHDRLIST *dirPtr;
  842.  
  843. /* The current directory being handled by getFirstDir/getNextDir() */
  844.  
  845. static WORD currDir;
  846.  
  847. /* Add a directory header to the directory-by-directory list of its parent
  848.    directory */
  849.  
  850. void addDirHdrToList( DIRHDRLIST *theHeader )
  851.     {
  852.     int dirNo = theHeader->data.parentIndex;
  853.     DIRHDRLIST *dirHdrPrevPtr;
  854.  
  855.     if( dirNo > lastEntry )
  856.         {
  857.         /* Bad entry, move to ROOT_DIR */
  858.         dirNo = theHeader->data.parentIndex = ROOT_DIR;
  859.         arcdirCorrupted = TRUE;
  860.         }
  861.  
  862.     /* Update the dir head and tail pointers and link in the new header */
  863.     if( ( dirHdrPrevPtr = dirHdrList[ dirNo ]->dirInDirListTail ) == NULL )
  864.         /* Set up new list if necessary */
  865.         dirHdrList[ dirNo ]->dirInDirListHead = theHeader;
  866.     else
  867.         dirHdrPrevPtr->nextDir = theHeader;
  868.     dirHdrList[ dirNo ]->dirInDirListTail = theHeader;
  869.     theHeader->prevDir = dirHdrPrevPtr;
  870.     theHeader->nextDir = NULL;
  871.  
  872.     /* Clear directory links */
  873.     theHeader->nextLink = theHeader->prevLink = NULL;
  874.     }
  875.  
  876. /* Add a directory name to the list of directory names */
  877.  
  878. void addDirName( char *dirName, const LONG dirTime )
  879.     {
  880.     DIRHDRLIST *theHeader;
  881.  
  882.     if( ( theHeader = ( DIRHDRLIST * ) hmalloc( sizeof( DIRHDRLIST ) ) ) == NULL )
  883.         error( OUT_OF_MEMORY );
  884.     dirHdrList[ lastEntry + 1 ] = theHeader;
  885.     currDirHdrListIndex++;
  886.     if( currDirHdrListIndex >= MEM_BUFSIZE )
  887.         error( OUT_OF_MEMORY );
  888.  
  889.     /* Link in new header */
  890.     dirHdrList[ lastEntry ]->next = lastEntry + 1;
  891.     theHeader->data.dirInfo = 0;
  892.     theHeader->data.parentIndex = currEntry;
  893.     theHeader->data.dirTime = dirTime;
  894.     theHeader->data.dataLen = 0L;
  895.     theHeader->fileInDirListHead = theHeader->fileInDirListTail = NULL;
  896.     theHeader->dirInDirListHead = theHeader->dirInDirListTail = NULL;
  897.     theHeader->nextLink = theHeader->prevLink = NULL;
  898.     theHeader->offset = dirDataOffset;
  899.     theHeader->next = END_MARKER;
  900.     theHeader->dirIndex = lastEntry + 1;
  901.     theHeader->hType = DIRTYPE_NORMAL;
  902.     theHeader->linkID = NO_LINK;
  903.     theHeader->tagged = FALSE;
  904.  
  905.     /* Add the header to the directory-by-directory list */
  906.     lastEntry++;
  907.     currEntry = lastEntry;
  908.     addDirHdrToList( theHeader );
  909.  
  910. #if defined( __AMIGA__ ) || defined( __ARC__ ) || defined( __MAC__ ) || \
  911.     defined( __OS2__ ) || defined( __UNIX__ )
  912.     /* Check if we want to smash case */
  913.     if( sysSpecFlags & SYSPEC_FORCELOWER )
  914.         strlwr( dirName );
  915. #endif /* __AMIGA__ || __ARC__ || __MAC__ || __OS2__ || __UNIX__ */
  916.  
  917.     /* Add the directory name */
  918.     if( ( theHeader->dirName = ( char * ) hmalloc( strlen( dirName ) + 1 ) ) == NULL )
  919.         error( OUT_OF_MEMORY );
  920.     strcpy( theHeader->dirName, dirName );
  921.     }
  922.  
  923. /* Write out directory data tag */
  924.  
  925. int addDirData( WORD tagID, const int store, LONG dataLength, LONG uncoprLength )
  926.     {
  927.     int headerLen;
  928.  
  929.     if( dirFileFD == IO_ERROR )
  930.         {
  931.         /* Create a temporary file to hold any relevant directory
  932.            information if necessary.  We could do this automatically in
  933.            FRONTEND.C, but by doing it here we only create it if there
  934.            is a need to */
  935.         strcpy( dirFileName, archiveFileName );
  936.         strcpy( dirFileName + strlen( dirFileName ) - 3, DIRTEMP_EXT );
  937.         if( ( dirFileFD = hcreat( dirFileName, CREAT_ATTR ) ) == IO_ERROR )
  938.             error( CANNOT_OPEN_TEMPFILE );
  939.         }
  940.  
  941.     /* Write the tag and update the archive directory information */
  942.     headerLen = writeDirTag( tagID, store, dataLength, uncoprLength );
  943.     dataLength += headerLen;
  944.     dirHdrList[ currEntry ]->data.dataLen += dataLength;
  945.     dirDataOffset += dataLength;
  946.     return( headerLen );
  947.     }
  948.  
  949. /* Move to the start of the directory data in the archive */
  950.  
  951. void movetoDirData( void )
  952.     {
  953.     int dummy;
  954.  
  955.     /* Move to start of directory data plus the offset of the current directory */
  956.     vlseek( getCoprDataLength() + dirHdrList[ currDir ]->offset, SEEK_SET );
  957.     resetFastIn();
  958.  
  959.     /* Process the encryption header if necessary */
  960.     if( ( cryptFlags & ( CRYPT_CKE_ALL | CRYPT_PKE_ALL ) ) && \
  961.             !cryptSetInput( dirDataOffset, &dummy ) )
  962.         error( CANNOT_PROCESS_CRYPT_ARCH );
  963.     }
  964.  
  965. /* Set the current archive directory */
  966.  
  967. inline void setArchiveCwd( const WORD dirNo )
  968.     {
  969.     currEntry = currDir = dirNo;
  970.     }
  971.  
  972. /* Go back up one directory level in the dirNameIndex */
  973.  
  974. inline void popDir( void )
  975.     {
  976.     currEntry = dirHdrList[ currEntry ]->data.parentIndex;
  977.     }
  978.  
  979. /* Return the parent of the current directory */
  980.  
  981. inline WORD getParent( const WORD dirNo )
  982.     {
  983.     return( dirHdrList[ dirNo ]->data.parentIndex );
  984.     }
  985.  
  986. /* Return the name of the specified directory */
  987.  
  988. inline char *getDirName( const WORD dirNo )
  989.     {
  990.     return( dirHdrList[ dirNo ]->dirName );
  991.     }
  992.  
  993. /* Return the creation date of the specified directory */
  994.  
  995. inline LONG getDirTime( const WORD dirNo )
  996.     {
  997.     return( dirHdrList[ dirNo ]->data.dirTime );
  998.     }
  999.  
  1000. /* Set the creation date of the specified directory */
  1001.  
  1002. inline void setDirTime( const WORD dirNo, const LONG dirTime )
  1003.     {
  1004.     dirHdrList[ dirNo ]->data.dirTime = dirTime;
  1005.     }
  1006.  
  1007. /* Return the length of the specified directories data */
  1008.  
  1009. inline LONG getDirAuxDatalen( const WORD dirNo )
  1010.     {
  1011.     return( dirHdrList[ dirNo ]->data.dataLen );
  1012.     }
  1013.  
  1014. /* Get the tagged status of the specified directory */
  1015.  
  1016. inline BOOLEAN getDirTaggedStatus( const WORD dirNo )
  1017.     {
  1018.     return( dirHdrList[ dirNo ]->tagged );
  1019.     }
  1020.  
  1021. /* Set the tagged status of the specified directory */
  1022.  
  1023. inline void setDirTaggedStatus( const WORD dirNo )
  1024.     {
  1025.     dirHdrList[ dirNo ]->tagged = TRUE;
  1026.     }
  1027.  
  1028. /* Sort the files in a directory by name.  This routine uses a modified
  1029.    insertion sort whose runtime is proportional to the number of files out of
  1030.    order.  This algorithm runs in constant time when the files are mostly in
  1031.    order, which is usually the case.  In contrast a more advanced algorithm
  1032.    like quicksort would run in O( n^2 ) time and require O( n ) stack space
  1033.    for recursion in a similar situation.  The best algorithm is actually a
  1034.    mergesort, but the code is more than twice as long */
  1035.  
  1036. void sortFiles( const WORD dirNo )
  1037.     {
  1038.     FILEHDRLIST *oldStartPtr = dirHdrList[ dirNo ]->fileInDirListHead;
  1039.     FILEHDRLIST *newStartPtr, *newEndPtr;
  1040.     FILEHDRLIST *oldCurrent, *newCurrent, *prev;
  1041.     char *fileName;
  1042.  
  1043.     newEndPtr = newStartPtr = oldStartPtr;
  1044.     if( newStartPtr != NULL )
  1045.         {
  1046.         /* Build first node of new list */
  1047.         oldCurrent = oldStartPtr->nextFile;
  1048.         newStartPtr->nextFile = NULL;
  1049.  
  1050.         /* Copy the old list across to the new list, sorting it as we go */
  1051.         while( oldCurrent != NULL )
  1052.             {
  1053.             fileName = oldCurrent->fileName;
  1054.             if( strcmp( newEndPtr->fileName, fileName ) < 0 )
  1055.                 {
  1056.                 /* Filename is in order, just append node to end of new list */
  1057.                 newCurrent = oldCurrent;
  1058.                 oldCurrent = oldCurrent->nextFile;    /* Go to next node in old list */
  1059.  
  1060.                 /* Link node to end of list and set newEnd ptr */
  1061.                 newEndPtr->nextFile = newCurrent;
  1062.                 newCurrent->prevFile = newEndPtr;
  1063.                 newCurrent->nextFile = NULL;
  1064.                 newEndPtr = newCurrent;
  1065.                 }
  1066.             else
  1067.                 {
  1068.                 /* Find correct position in new list to insert node from old list */
  1069.                 prev = newCurrent = newStartPtr;
  1070.                 while( ( newCurrent != NULL ) && strcmp( newCurrent->fileName, fileName ) < 0 )
  1071.                     {
  1072.                     prev = newCurrent;
  1073.                     newCurrent = newCurrent->nextFile;
  1074.                     }
  1075.  
  1076.                 /* Remember end of new list for shortcut add */
  1077.                 if( newCurrent == NULL )
  1078.                     newEndPtr = oldCurrent;
  1079.  
  1080.                 /* Link node from old list into new list */
  1081.                 if( prev == newCurrent )
  1082.                     {
  1083.                     /* At start of list, set node to new list start */
  1084.                     newStartPtr = oldCurrent;
  1085.                     newStartPtr->prevFile = NULL;
  1086.                     }
  1087.                 else
  1088.                     {
  1089.                     /* Add link to previous node */
  1090.                     prev->nextFile = oldCurrent;
  1091.                     oldCurrent->prevFile = prev;
  1092.                     }
  1093.  
  1094.                 /* Add link to next node */
  1095.                 prev = oldCurrent->nextFile;    /* Use prev for temporary storage */
  1096.                 oldCurrent->nextFile = newCurrent;
  1097.                 newCurrent->prevFile = oldCurrent;
  1098.                 oldCurrent = prev;
  1099.                 }
  1100.             }
  1101.  
  1102.         /* Set fileInDirListHead to point to start of sorted list */
  1103.         dirHdrList[ dirNo ]->fileInDirListHead = newStartPtr;
  1104.         }
  1105.     }
  1106.  
  1107. /* Return the first entry in the file-in-directory list, or NULL if no files */
  1108.  
  1109. inline FILEHDRLIST *getFirstFileEntry( const WORD dirNo )
  1110.     {
  1111.     return( filePtr = dirHdrList[ dirNo ]->fileInDirListHead );
  1112.     }
  1113.  
  1114. /* Return the next entry in the file-in-directory list, or NULL if none */
  1115.  
  1116. inline FILEHDRLIST *getNextFileEntry( void )
  1117.     {
  1118.     return( filePtr = filePtr->nextFile );
  1119.     }
  1120.  
  1121. #ifdef GUI
  1122.  
  1123. /* Return the previous entry in the file-in-directory list, or NULL if none */
  1124.  
  1125. inline FILEHDRLIST *getPrevFileEntry( void )
  1126.     {
  1127.     return( filePtr = filePtr->prevFile );
  1128.     }
  1129. #endif /* GUI */
  1130.  
  1131. /* Return the first entry in the directory-in-directory list, or NULL if no files */
  1132.  
  1133. inline DIRHDRLIST *getFirstDirEntry( const WORD dirNo )
  1134.     {
  1135.     return( dirPtr = dirHdrList[ dirNo ]->dirInDirListHead );
  1136.     }
  1137.  
  1138. /* Return the next entry in the directory-in-directory list, or NULL if none */
  1139.  
  1140. inline DIRHDRLIST *getNextDirEntry( void )
  1141.     {
  1142.     return( dirPtr = dirPtr->nextDir );
  1143.     }
  1144.  
  1145. #ifdef GUI
  1146.  
  1147. /* Return the previous entry in the directory-in-directory list, or NULL if none */
  1148.  
  1149. inline DIRHDRLIST *getPrevDirEntry( void )
  1150.     {
  1151.     return( dirPtr = dirPtr->prevDir );
  1152.     }
  1153. #endif /* GUI */
  1154.  
  1155. /* Return the first directory in the directory tree */
  1156.  
  1157. inline WORD getFirstDir( void )
  1158.     {
  1159.     currDir = ROOT_DIR;
  1160.     return( ROOT_DIR );
  1161.     }
  1162.  
  1163. /* Return the next directory in the directory tree */
  1164.  
  1165. inline WORD getNextDir( void )
  1166.     {
  1167.     currDir = dirHdrList[ currDir ]->next;
  1168.     return( currDir );
  1169.     }
  1170.  
  1171. /* Determine whether a given path is contained in the archives directory
  1172.    structure.  It would be easier to do the matching backwards since a
  1173.    directory can only ever have one parent and so we don't have to search a
  1174.    list of directory names; however this won't work because we don't know in
  1175.    advance the directory index of the bottom directory */
  1176.  
  1177. int matchPath( const char *pathName, const int pathLen, WORD *pathDirIndex )
  1178.     {
  1179.     BOOLEAN matched, hasSlash = ( *pathName == SLASH ) ? TRUE : FALSE;
  1180.     int startIndex = hasSlash, endIndex, segmentLen;
  1181.     WORD currDirIndex = ROOT_DIR;
  1182.     DIRHDRLIST *currDirPtr, *prevDirPtr;
  1183.     char *dirName;
  1184.  
  1185.     *pathDirIndex = ROOT_DIR;    /* Initially we're at the root directory */
  1186.  
  1187.     /* Check for the trivial case of the path being to the archive's root
  1188.        directory */
  1189.     if( !pathLen )
  1190.         return( PATH_FULLMATCH );
  1191.  
  1192.     while( TRUE )
  1193.         {
  1194.         /* Extract a directory name out of the pathname */
  1195.         endIndex = startIndex;
  1196.         while( endIndex < pathLen && pathName[ endIndex ] != SLASH )
  1197.             endIndex++;
  1198.  
  1199.         /* Now search through the list of subdirectories in this directory
  1200.            trying to find a match */
  1201.         currDirPtr = dirHdrList[ currDirIndex ]->dirInDirListHead;
  1202.         segmentLen = endIndex - startIndex;
  1203.         matched = FALSE;
  1204.         if( currDirPtr != NULL )
  1205.             do
  1206.                 {
  1207.                 dirName = currDirPtr->dirName;
  1208.  
  1209.                 /* We must not only check that the two directory names are
  1210.                    equal but also that they are of the same length, since
  1211.                    we could get an erroneous match if one directory name is
  1212.                    merely a prefix of another */
  1213.                 if( !caseStrncmp( pathName + startIndex, dirName, segmentLen ) && \
  1214.                                 segmentLen == strlen( dirName ) )
  1215.                     {
  1216.                     matched = TRUE;
  1217.                     break;
  1218.                     }
  1219.                 }
  1220.             while( ( currDirPtr = currDirPtr->nextDir ) != NULL );
  1221.  
  1222.         if( endIndex == pathLen || !matched )
  1223.             /* We've reached the end of the pathname */
  1224.             break;
  1225.  
  1226.         startIndex = endIndex + 1;        /* +1 skips SLASH */
  1227.         prevDirPtr = currDirPtr;
  1228.         currDirIndex = currDirPtr->dirIndex;
  1229.         }
  1230.  
  1231.     /* Determine the nature of the match */
  1232.     if( matched )
  1233.         {
  1234.         /* Full match: currDirPtr contains the directory index */
  1235.         *pathDirIndex = currDirPtr->dirIndex;
  1236.         return( PATH_FULLMATCH );
  1237.         }
  1238.     else
  1239.         if( startIndex == hasSlash )
  1240.             /* No match at all */
  1241.             return( PATH_NOMATCH );
  1242.         else
  1243.             {
  1244.             /* Partial match: prevDirPtr contains last matched directory */
  1245.             *pathDirIndex = prevDirPtr->dirIndex;
  1246.             return( PATH_PARTIALMATCH );
  1247.             }
  1248.     }
  1249.  
  1250. /* Clean up the archive directory tree by first turning it into a correct
  1251.    depth-first traversal with contiguous directory indices (note that since
  1252.    this procedure rearranges the order of the directories within the archive
  1253.    it will destroy the correspondence of the dirData to the directory
  1254.    entries), and then traversing the file and directory lists and setting the
  1255.    link ID's to contiguous values */
  1256.  
  1257. #define MAX_DIR_NESTING    50    /* Shouldn't get > 50 nested directories */
  1258.  
  1259. void fixEverything( void )
  1260.     {
  1261.     BOOLEAN skipFind = FALSE, moreDirs = TRUE, isAnchorNode;
  1262.     FILEHDRLIST *fileInfo, *fileHdrCursor, *savedFilePtr = filePtr;
  1263.     DIRHDRLIST *dirPtr, *dirPtrStack[ MAX_DIR_NESTING ];
  1264.     WORD dirStack[ MAX_DIR_NESTING ], dirNo;
  1265.     int currentLevel = 0, currCount = 0, lastCount = 0;
  1266.  
  1267.     /* First, clean up the directory structure */
  1268.     if( ( dirPtr = dirHdrList[ ROOT_DIR ]->dirInDirListHead ) == NULL )
  1269.         moreDirs = FALSE;
  1270.     else
  1271.         skipFind = TRUE;
  1272.  
  1273.     lastEntry = ROOT_DIR;
  1274.     while( TRUE )
  1275.         if( moreDirs && ( skipFind || ( dirPtr = dirPtr->nextDir ) != NULL ) )
  1276.             {
  1277.             skipFind = FALSE;    /* It's 3am - do you know where your brain is? */
  1278.             dirPtrStack[ currentLevel ] = dirPtr;
  1279.             dirStack[ currentLevel++ ] = currCount;
  1280.             if( currentLevel > MAX_DIR_NESTING )
  1281.                 error( TOO_MANY_LEVELS_NESTING );
  1282.  
  1283.             /* Set correct directory number and rebuild chain of directories
  1284.                in correct order */
  1285.             dirNo = dirPtr->dirIndex;
  1286.             dirHdrList[ dirNo ]->data.parentIndex = currCount;
  1287.             lastCount++;
  1288.             currCount = lastCount;
  1289.             dirHdrList[ lastEntry ]->next = dirNo;
  1290.             lastEntry = dirNo;
  1291.  
  1292.             /* Process all files in this directory.  We never bother doing
  1293.                ROOT_DIR since all files in this automatically have the correct
  1294.                directory index */
  1295.             if( ( fileInfo = getFirstFileEntry( dirNo ) ) != NULL )
  1296.                 do
  1297.                     fileInfo->data.dirIndex = lastCount;
  1298.                 while( ( fileInfo = getNextFileEntry() ) != NULL );
  1299.  
  1300.             if( ( dirPtr = dirHdrList[ dirNo ]->dirInDirListHead ) == NULL )
  1301.                 moreDirs = FALSE;
  1302.             else
  1303.                 skipFind = TRUE;
  1304.             }
  1305.         else
  1306.             if( currentLevel )
  1307.                 {
  1308.                 dirPtr = dirPtrStack[ --currentLevel ];
  1309.                 currCount = dirStack[ currentLevel ];
  1310.                 moreDirs = TRUE;
  1311.                 }
  1312.             else
  1313.                 break;
  1314.  
  1315.     dirHdrList[ lastEntry ]->next = END_MARKER;
  1316.     filePtr = savedFilePtr;
  1317.  
  1318.     /* Now clean up directory links if there are any */
  1319.     if( dirLinkNo > NO_LINK + 1 )
  1320.         {
  1321.         /* Reset dirLinkNo to lowest possible link value */
  1322.         dirLinkNo = NO_LINK + 1;
  1323.  
  1324.         /* Step through the list of dir.headers looking for linked directories */
  1325.         for( dirNo = dirHdrList[ ROOT_DIR ]->next; dirNo != END_MARKER; \
  1326.              dirNo = dirHdrList[ dirNo ]->next )
  1327.             if( getLinkID( dirHdrList[ dirNo ]->linkID ) != NO_LINK )
  1328.                 {
  1329.                 dirPtr = dirHdrList[ dirNo ];
  1330.  
  1331.                 /* If there are previous links in the chain, set the link ID
  1332.                    to that of the previous link.  Otherwise, add a new link ID */
  1333.                 isAnchorNode = isLinkAnchor( dirPtr->linkID );
  1334.                 if( dirPtr->prevLink != NULL )
  1335.                     dirPtr->linkID = dirPtr->prevLink->linkID;
  1336.                 else
  1337.                     dirPtr->linkID = dirLinkNo++;
  1338.  
  1339.                 /* Mark new link ID as an anchor node if necessary */
  1340.                 if( isAnchorNode )
  1341.                     dirPtr->linkID |= LINK_ANCHOR;
  1342.                 }
  1343.         }
  1344.  
  1345.     /* Finally, clean up any file links */
  1346.     if( fileLinkNo > NO_LINK + 1 )
  1347.         {
  1348.         /* Reset fileLinkNo to lowest possible link value */
  1349.         fileLinkNo = NO_LINK + 1;
  1350.  
  1351.         /* Step through the list of file headers looking for linked files */
  1352.         for( fileHdrCursor = fileHdrStartPtr; fileHdrCursor != NULL; \
  1353.              fileHdrCursor = fileHdrCursor->next )
  1354.             if( getLinkID( fileHdrCursor->linkID ) != NO_LINK )
  1355.                 {
  1356.                 /* If there are previous links in the chain, set the link ID
  1357.                    to that of the previous link.  Otherwise, add a new link ID */
  1358.                 isAnchorNode = isLinkAnchor( fileHdrCursor->linkID );
  1359.                 if( fileHdrCursor->prevLink != NULL )
  1360.                     fileHdrCursor->linkID = fileHdrCursor->prevLink->linkID;
  1361.                 else
  1362.                     fileHdrCursor->linkID = fileLinkNo++;
  1363.  
  1364.                 /* Mark new link ID as an anchor node if necessary */
  1365.                 if( isAnchorNode )
  1366.                     fileHdrCursor->linkID |= LINK_ANCHOR;
  1367.                 }
  1368.         }
  1369.     }
  1370.  
  1371. /* Select/unselect a file */
  1372.  
  1373. void selectFile( FILEHDRLIST *fileHeader, BOOLEAN status )
  1374.     {
  1375.     fileHeader->tagged = status;
  1376.     }
  1377.  
  1378. /* Select/unselect all subdirectories and files in a directory.
  1379.    selectDir() takes a DIRHDRLIST entry as its argument, selectDirNo()
  1380.    takes a directory ID */
  1381.  
  1382. void selectDirNo( const WORD dirNo, BOOLEAN status )
  1383.     {
  1384.     selectDir( dirHdrList[ dirNo ], status );
  1385.     }
  1386.  
  1387. void selectDir( DIRHDRLIST *dirHeader, BOOLEAN status )
  1388.     {
  1389.     BOOLEAN skipFind = FALSE, moreDirs = TRUE;
  1390.     FILEHDRLIST *fileInfo, *savedFilePtr = filePtr;
  1391.     DIRHDRLIST *dirPtr, *dirPtrStack[ MAX_DIR_NESTING ];
  1392.     int dirNo, currentLevel = 0;
  1393.  
  1394.     /* Tag the directory itself and its files */
  1395.     dirHeader->tagged = status;
  1396.     if( ( fileInfo = getFirstFileEntry( dirHeader->dirIndex ) ) != NULL )
  1397.         do
  1398.             fileInfo->tagged = status;
  1399.         while( ( fileInfo = getNextFileEntry() ) != NULL );
  1400.  
  1401.     /* Now tag any subdirectories and their files */
  1402.     if( ( dirPtr = dirHeader->dirInDirListHead ) == NULL )
  1403.         moreDirs = FALSE;
  1404.     else
  1405.         skipFind = TRUE;
  1406.  
  1407.     while( TRUE )
  1408.         if( moreDirs && ( skipFind || ( dirPtr = dirPtr->nextDir ) != NULL ) )
  1409.             {
  1410.             skipFind = FALSE;
  1411.             dirPtrStack[ currentLevel++ ] = dirPtr;
  1412.             if( currentLevel > MAX_DIR_NESTING )
  1413.                 error( TOO_MANY_LEVELS_NESTING );
  1414.             dirPtr->tagged = status;
  1415.  
  1416.             /* Process all files in this directory */
  1417.             dirNo = dirPtr->dirIndex;
  1418.             if( ( fileInfo = getFirstFileEntry( dirNo ) ) != NULL )
  1419.                 do
  1420.                     fileInfo->tagged = status;
  1421.                 while( ( fileInfo = getNextFileEntry() ) != NULL );
  1422.  
  1423.             if( ( dirPtr = dirHdrList[ dirNo ]->dirInDirListHead ) == NULL )
  1424.                 moreDirs = FALSE;
  1425.             else
  1426.                 skipFind = TRUE;
  1427.             }
  1428.         else
  1429.             if( currentLevel )
  1430.                 {
  1431.                 dirPtr = dirPtrStack[ --currentLevel ];
  1432.                 moreDirs = TRUE;
  1433.                 }
  1434.             else
  1435.                 break;
  1436.  
  1437.     filePtr = savedFilePtr;
  1438.     }
  1439.  
  1440. /* Select only the immediate contents of a directory */
  1441.  
  1442. void selectDirNoContents( const WORD dirNo, BOOLEAN status )
  1443.     {
  1444.     FILEHDRLIST *fileInfo, *savedFilePtr = filePtr;
  1445.     DIRHDRLIST *dirInfo, *savedDirPtr = dirPtr;
  1446.  
  1447.     /* Tag the files within the directory */
  1448.     if( ( fileInfo = getFirstFileEntry( dirNo ) ) != NULL )
  1449.         do
  1450.             fileInfo->tagged = status;
  1451.         while( ( fileInfo = getNextFileEntry() ) != NULL );
  1452.     filePtr = savedFilePtr;
  1453.  
  1454.     /* Tag any directories within it */
  1455.     if( ( dirInfo = getFirstDirEntry( dirNo ) ) != NULL )
  1456.         do
  1457.             dirInfo->tagged = status;
  1458.         while( ( dirInfo = getNextDirEntry() ) != NULL );
  1459.     dirPtr = savedDirPtr;
  1460.     }
  1461.  
  1462. /* Calculate the size of a header as it will be written to disk */
  1463.  
  1464. int computeHeaderSize( const FILEHDR *theHeader, const BYTE extraInfo )
  1465.     {
  1466.     int headerSize = sizeof( WORD ) + sizeof( LONG );
  1467.  
  1468.     /* Evaluate size of main header */
  1469.     headerSize += ( theHeader->fileLen > 0xFFFF ) ? \
  1470.         sizeof( LONG ) : sizeof( WORD );
  1471.     headerSize += ( theHeader->dataLen > 0xFFFF ) ? \
  1472.         sizeof( LONG ) : sizeof( WORD );
  1473.     headerSize += ( theHeader->dirIndex > 0xFF ) ? sizeof( WORD ) + sizeof( WORD ) : \
  1474.                   ( theHeader->auxDataLen > 0xFF ) ? sizeof( BYTE ) + sizeof( WORD ) : \
  1475.                   ( theHeader->dirIndex || theHeader->auxDataLen ) ? \
  1476.                     sizeof( BYTE ) + sizeof( BYTE ) : 0;
  1477.  
  1478.     /* Evaluate size of extraInfo information */
  1479.     headerSize += getExtraInfoLen( extraInfo );
  1480.     if( extraInfo )
  1481.         headerSize++;    /* Allow for size of extraInfo byte */
  1482.  
  1483.     return( headerSize );
  1484.     }
  1485.